package com.expert.miniapp.util;

import org.springframework.util.StringUtils;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * Utility helpers for converting map keys to snake_case while preserving value structure.
 */
public final class SnakeCaseUtils {

    private SnakeCaseUtils() {
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> toSnakeCaseMap(Map<String, Object> source) {
        if (source == null || source.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<String, Object> result = new LinkedHashMap<>(source.size());
        for (Map.Entry<String, Object> entry : source.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            String snakeKey = key == null ? null : toSnakeCase(key);
            result.put(snakeKey, transformValue(value));
        }
        return result;
    }

    private static Object transformValue(Object value) {
        if (value instanceof Map<?, ?> mapValue) {
            Map<String, Object> nested = new LinkedHashMap<>(mapValue.size());
            for (Map.Entry<?, ?> entry : mapValue.entrySet()) {
                Object nestedKey = entry.getKey();
                String snakeKey = nestedKey == null ? null : toSnakeCase(String.valueOf(nestedKey));
                nested.put(snakeKey, transformValue(entry.getValue()));
            }
            return nested;
        }

        if (value instanceof Collection<?> collection) {
            List<Object> converted = new ArrayList<>(collection.size());
            for (Object element : collection) {
                converted.add(transformValue(element));
            }
            return converted;
        }

        if (value != null && value.getClass().isArray()) {
            int length = Array.getLength(value);
            List<Object> list = new ArrayList<>(length);
            for (int i = 0; i < length; i++) {
                list.add(transformValue(Array.get(value, i)));
            }
            return list;
        }

        return value;
    }

    private static String toSnakeCase(String input) {
        if (!StringUtils.hasText(input)) {
            return input;
        }
        String trimmed = input.trim();
        String replaced = trimmed.replaceAll("[\\s\\-]+", "_");
        String separated = replaced
                .replaceAll("([a-z\\d])([A-Z])", "$1_$2")
                .replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2");
        return separated.toLowerCase(Locale.ROOT);
    }
}

